using System.Collections;
using System.Collections.Generic;
using System.Text;
using Unity.Profiling;
using Unity.Profiling.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Profiling;

/// <summary>
/// 使用 ProfilerRecorder 访问 Profiler 公开的性能指标。
/// 您可以使用它来以统一的方式读取 Profiler 计数器数据（例如内存或渲染统计信息）以及 Profiler 标记计时数据。
/// </summary>
public class ProfilerController : MonoBehaviour
{
    struct StatInfo
    {
        public ProfilerCategory Cat;
        public string Name;
        public ProfilerMarkerDataUnit Unit;
    }

    /// <summary>
    /// 获取可通过 ProfilerRecorder 访问的所有可用句柄。
    /// </summary>
    private class ProfilerHanler
    {
       public static Dictionary<string,StatInfo> EnumerateProfilerStats()
        {
            var aailableStatHanles = new List<ProfilerRecorderHandle>();
            ProfilerRecorderHandle.GetAvailable(aailableStatHanles);

            var availableStats = new Dictionary<string, StatInfo>(aailableStatHanles.Count);
            foreach(var h in aailableStatHanles)
            {
                var statDesc = ProfilerRecorderHandle.GetDescription(h);
                var statInfo = new StatInfo()
                {
                    Cat = statDesc.Category,
                    Name = statDesc.Name,
                    Unit = statDesc.UnitType
                };
                if(!availableStats.ContainsKey(statInfo.Name)) availableStats.Add(statInfo.Name,statInfo);
                else
                {
                    Debug.Log("Warning: has exist Key: "+ $"{statInfo.Name} - {(int)statInfo.Cat}-  {statInfo.Unit}");
                }
            }
            return availableStats;
            //var sb = new StringBuilder("Available stats:\n");
            //foreach (var s in availableStats)
            //{
            //    sb.AppendLine($"{(int)s.Cat}\t\t - {s.Name}\t\t - {s.Unit}");
            //}

            //Debug.Log(sb.ToString());
        }
    }


    string statsText;
    ProfilerRecorder systemMemoryRecorder;
    ProfilerRecorder gcMemoryRecorder;
    ProfilerRecorder mainThreadTimeRecorder;
    private Dictionary<string, StatInfo> statDict;
    private Dictionary<StatInfo, ProfilerRecorder> Recorders = new Dictionary<StatInfo, ProfilerRecorder>();

   
    GUIStyle fontStyle;
    private void Start()
    {       

    }

    void OnEnable()
    {
        //systemMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "System Used Memory");
        //gcMemoryRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Reserved Memory");
        //mainThreadTimeRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Internal, "Main Thread", 15);

        statDict = ProfilerHanler.EnumerateProfilerStats();
        
        AddMemoryProfiler("System Used Memory");
        AddMemoryProfiler("Total Reserved Memory");
        AddMemoryProfiler("Total Used Memory");
        AddMemoryProfiler("GC Reserved Memory");
        AddMemoryProfiler("GC Used Memory");
        AddMemoryProfiler("Gfx Used Memory");
        AddProfiler("Main Thread", ProfilerCategory.Render, ProfilerMarkerDataUnit.TimeNanoseconds); //cpu time

        AddProfiler("Texture Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
        AddMemoryProfiler("Texture Memory");
        AddProfiler("Material Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
        AddMemoryProfiler("Material Memory");
        AddProfiler("Mesh Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
        AddMemoryProfiler("Mesh Memory");
        AddMemoryProfiler("Profiler Used Memory");
        AddProfiler("GameObject Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
        AddProfiler("Object Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
        AddProfiler("Asset Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);
        AddProfiler("Scene Object Count", ProfilerCategory.Memory, ProfilerMarkerDataUnit.Count);

        AddProfiler("Draw Calls Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
        AddProfiler("Batches Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
        AddProfiler("SetPass Calls Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
        AddProfiler("Triangles Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
        AddProfiler("Vertices Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
        AddProfiler("Used Buffers Count", ProfilerCategory.Render, ProfilerMarkerDataUnit.Count);
        AddProfiler("Used Buffers Bytes", ProfilerCategory.Render, ProfilerMarkerDataUnit.Bytes);
     
        AddProfiler("Camera.Render", ProfilerCategory.Render, ProfilerMarkerDataUnit.TimeNanoseconds);
        AddProfiler("RenderLoop.Draw", ProfilerCategory.Render, ProfilerMarkerDataUnit.TimeNanoseconds);

        //foreach (var mesh in meshes)
        //{
        //    long size = Profiler.GetRuntimeMemorySizeLong(mesh);
        //    sb.AppendLine($"{mesh.GetType()}_{mesh.name}: {GetSizeString(size)}");
        //}

    }


    void OnDisable()
    {
        //systemMemoryRecorder.Dispose();
        //gcMemoryRecorder.Dispose();
        //mainThreadTimeRecorder.Dispose();

        foreach (StatInfo info in Recorders.Keys)
        {
            ProfilerRecorder recorder = Recorders[info];
            recorder.Dispose();
        }
        Recorders.Clear();

    }

    void Update()
    {
        var sb = new StringBuilder(500);
        //sb.AppendLine($"Frame Time: {GetRecorderFrameAverage(mainThreadTimeRecorder) * (1e-6f):F1} ms");
        //sb.AppendLine($"GC Memory: {gcMemoryRecorder.LastValue / (1024 * 1024)} MB");
        //sb.AppendLine($"System Memory: {systemMemoryRecorder.LastValue / (1024 * 1024)} MB");
        //statsText = sb.ToString();

        foreach (StatInfo info in Recorders.Keys)
        {
            ProfilerRecorder recorder = Recorders[info];
            if (info.Unit == ProfilerMarkerDataUnit.Bytes)
                sb.AppendLine($"{info.Name}: {recorder.LastValue / (1024f * 1024f):F2} MB");
            else if (info.Unit == ProfilerMarkerDataUnit.TimeNanoseconds)
                sb.AppendLine($"{info.Name}: {GetRecorderFrameAverage(recorder) * (1e-6f):F2} ms");
            else
                sb.AppendLine($"{info.Name}: {recorder.LastValue} ");
        }
        statsText = sb.ToString();


    }

    void OnGUI()
    {
        if(fontStyle == null)
        {
            fontStyle = new GUIStyle(GUI.skin.textArea);
            //fontStyle.normal.textColor = new Color(1, 0, 0);   //设置字体颜色
            // 设置字体大小
            fontStyle.fontSize = 20;
        }


        GUI.TextArea(new Rect(410, 10, 400, 800), statsText, fontStyle);
       
    }


    static double GetRecorderFrameAverage(ProfilerRecorder recorder)
    {
        var samplesCount = recorder.Capacity;
        if (samplesCount == 0) return 0;
        double r = 0;
        unsafe
        {
            var samples = stackalloc ProfilerRecorderSample[samplesCount];
            recorder.CopyTo(samples, samplesCount);
            for (var i = 0; i < samplesCount; i++)
            {
                r += samples[i].Value;
            }
            r /= samplesCount;
        }

        return r;
    }

    private void AddProfiler(string statName)
    {
        if (statDict == null)
        {
            statDict = ProfilerHanler.EnumerateProfilerStats();
        }

        if (statDict.ContainsKey(statName))
        {
            var info = statDict[statName];
            var recorder = ProfilerRecorder.StartNew(info.Cat, statName);
            Recorders.Add(info, recorder);
        }
        else
        {
            Debug.LogError("No StatName:" + statName);
        }
    }
    private void AddProfiler(string statName, ProfilerCategory pc, ProfilerMarkerDataUnit unit)
    {
        if (statDict == null)
        {
            statDict = ProfilerHanler.EnumerateProfilerStats();
        }

        if (statDict.ContainsKey(statName))
        {
            var info = statDict[statName];
            var recorder = ProfilerRecorder.StartNew(info.Cat, statName);
            Recorders.Add(info, recorder);
        }
        else
        {
            Debug.Log("No StatName:" + statName);
        }
    }
    private void AddMemoryProfiler(string statName)
    {
        AddProfiler(statName, ProfilerCategory.Memory, ProfilerMarkerDataUnit.Bytes);
    }

    public static string GetSizeString(long size)
    {
        if (size < 1024)
        {
            return $"{size} B";
        }
        else if (size < 1048576)//1024*1024
        {
            return $"{size / (1024f):F2} KB";
        }
        else
        {
            return $"{size / (1048576f):F2} MB";
        }
    }

}


